<template>
  <div class="attractions">
    <!-- 页面标题 -->
    <div class="page-header">
      <h1 class="page-title">
        <el-icon><MapLocation /></el-icon>
        景点浏览
      </h1>
      <p class="page-subtitle">探索南昌的美丽景点，发现更多精彩</p>
    </div>

    <!-- 顶部操作栏 -->
    <el-card class="action-bar">
      <el-row :gutter="20" align="middle">
        <el-col :span="24">
          <el-row :gutter="12" align="middle">
            <el-col :xs="24" :sm="12" :md="8">
              <div class="search-wrapper">
                <el-icon class="search-icon"><Search /></el-icon>
                <el-input
                  v-model="searchKeyword"
                  placeholder="搜索景点名称、地址..."
                  style="width: 100%"
                  clearable
                  size="large"
                  @input="handleSearchDebounced"
                  class="search-input"
                >
                  <template #prefix>
                    <el-icon><Search /></el-icon>
                  </template>
                </el-input>
              </div>
            </el-col>
            <el-col :xs="12" :sm="6" :md="4">
              <el-select
                v-model="selectedCategory"
                placeholder="全部分类"
                clearable
                style="width: 100%"
                size="large"
                @change="handleCategoryChange"
                class="filter-select"
              >
                <el-option label="全部" value="" />
                <el-option label="文化古迹" value="cultural" />
                <el-option label="自然风光" value="natural" />
                <el-option label="现代建筑" value="modern" />
                <el-option label="美食购物" value="food" />
              </el-select>
            </el-col>
            <el-col :xs="12" :sm="6" :md="4">
              <el-select
                v-model="sortBy"
                placeholder="排序方式"
                style="width: 100%"
                size="large"
                @change="handleSortChange"
                class="filter-select"
              >
                <el-option label="默认排序" value="" />
                <el-option label="评分最高" value="rating" />
                <el-option label="热度最高" value="popularity" />
                <el-option label="价格最低" value="price_asc" />
                <el-option label="价格最高" value="price_desc" />
                <el-option label="最新添加" value="newest" />
              </el-select>
            </el-col>
            <el-col :xs="24" :sm="12" :md="8" style="text-align: right;">
              <el-alert
                v-if="isAdmin"
                type="info"
                :closable="false"
                show-icon
                style="margin-bottom: 10px;"
              >
                <template #default>
                  <span>管理员请前往 <el-link type="primary" @click="$router.push('/admin/attractions')">管理后台</el-link> 管理景点</span>
                </template>
              </el-alert>
              <el-button type="primary" :icon="Refresh" @click="handleRefresh" class="refresh-button">
                刷新
              </el-button>
            </el-col>
          </el-row>
        </el-col>
      </el-row>
    </el-card>

    <!-- 批量操作栏 -->
    <el-card v-if="selectedAttractions.length > 0" class="batch-action-bar" shadow="hover">
      <div class="batch-action-content">
        <span class="batch-info">
          <el-icon><CircleCheck /></el-icon>
          已选择 <strong>{{ selectedAttractions.length }}</strong> 个景点
        </span>
        <div class="batch-actions">
          <el-button size="small" :icon="Star" @click="batchFavorite" :loading="batchFavoriting">
            批量收藏
          </el-button>
          <el-button size="small" :icon="Delete" @click="clearSelection">
            取消选择
          </el-button>
        </div>
      </div>
    </el-card>

    <!-- 景点卡片网格 -->
    <!-- 加载骨架屏 -->
    <div v-if="loading && attractions.length === 0" class="attractions-grid">
      <LoadingSkeleton v-for="i in 6" :key="i" type="card" />
    </div>
    
    <!-- 景点列表 -->
    <div v-else class="attractions-grid">
      <el-card
        v-for="attraction in attractions"
        :key="attraction.id"
        class="attraction-card"
        :class="{ 'selected': isSelected(attraction.id) }"
        shadow="hover"
        @click="handleCardClick(attraction, $event)"
      >
        <!-- 选择复选框 -->
        <div class="card-checkbox" @click.stop>
          <el-checkbox
            :model-value="isSelected(attraction.id)"
            @change="toggleSelection(attraction.id)"
            @click.stop
          />
        </div>
        <div class="card-image">
          <LazyImage
            v-if="attraction.primary_image"
            :src="attraction.primary_image"
            :preview-src-list="getPreviewImageList(attraction)"
            fit="cover"
            height="200"
            class="attraction-image"
            image-class="attraction-image"
          />
          <div v-else class="no-image">
            <el-icon><PictureRounded /></el-icon>
            <span>暂无图片</span>
          </div>
          <div class="card-badge">
            <el-tag :type="getCategoryType(attraction.category)" size="small">
              {{ attraction.category_display || '文化古迹' }}
            </el-tag>
          </div>
        </div>
        
        <div class="card-content">
          <h3 class="attraction-name" :title="attraction.name">
            {{ attraction.name }}
          </h3>
          <div class="attraction-info">
            <div class="info-item">
              <el-icon><Location /></el-icon>
              <span class="text-ellipsis">{{ attraction.address || '暂无地址' }}</span>
            </div>
            <div class="info-item">
              <el-icon><Money /></el-icon>
              <span>¥{{ attraction.ticket_price }}</span>
              <el-tag v-if="attraction.ticket_price == 0" type="success" size="small" style="margin-left: 5px;">
                免费
              </el-tag>
            </div>
            <div class="info-item">
              <el-icon><Clock /></el-icon>
              <span>{{ attraction.visit_time ? attraction.visit_time + '分钟' : '暂无' }}</span>
            </div>
          </div>
          
          <div class="card-footer">
            <div class="rating-section">
              <el-rate
                :model-value="(attraction.rating || 0) / 1"
                disabled
                show-score
                text-color="#ff9900"
                score-template="{value}"
                size="small"
              />
            </div>
            <div class="popularity-section">
              <el-progress
                :percentage="attraction.popularity || 0"
                :color="getPopularityColor(attraction.popularity)"
                :show-text="false"
                :stroke-width="4"
              />
              <span class="popularity-text">热度 {{ attraction.popularity || 0 }}</span>
            </div>
          </div>
        </div>
        
        <div class="card-actions" @click.stop>
          <el-button
            type="primary"
            size="small"
            :icon="ViewIcon"
            @click="viewDetail(attraction)"
          >
            详情
          </el-button>
          <el-button
            type="success"
            size="small"
            :icon="MapLocation"
            @click="viewOnMap(attraction)"
          >
            地图
          </el-button>
          <el-button
            v-if="isAuthenticated"
            :type="attraction.is_favorited ? 'warning' : 'default'"
            size="small"
            :icon="attraction.is_favorited ? StarFilled : Star"
            @click="toggleFavorite(attraction)"
            :loading="favoritingAttractions[attraction.id]"
            circle
            style="margin-left: auto;"
          />
        </div>
      </el-card>
    </div>

    <!-- 筛选和排序结果提示 -->
    <el-card v-if="(selectedCategory || sortBy || searchKeyword) && attractions.length > 0" class="filter-result-card" shadow="hover">
      <div class="filter-result-info">
        <el-icon><Filter /></el-icon>
        <span>已筛选 {{ total }} 个景点</span>
        <el-tag v-if="selectedCategory" type="info" size="small" style="margin-left: 8px;">
          {{ getCategoryDisplayName(selectedCategory) }}
        </el-tag>
        <el-tag v-if="sortBy" type="success" size="small" style="margin-left: 8px;">
          {{ getSortDisplayName(sortBy) }}
        </el-tag>
        <el-button
          type="text"
          size="small"
          @click="handleRefresh"
          style="margin-left: auto;"
        >
          清除筛选
        </el-button>
      </div>
    </el-card>

    <!-- 分页 -->
    <el-pagination
      v-if="total > pageSize"
      v-model:current-page="currentPage"
      v-model:page-size="pageSize"
      :total="total"
      :page-sizes="[12, 24, 48, 96]"
      layout="total, sizes, prev, pager, next, jumper"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      style="margin-top: 20px; justify-content: center;"
      class="pagination-custom"
    />


    <!-- 详情对话框 -->
    <el-dialog
      v-model="detailDialogVisible"
      :title="currentAttraction?.name"
      width="800px"
    >
      <div v-if="currentAttraction">
        <el-descriptions :column="1" border>
          <el-descriptions-item label="景点名称">
            {{ currentAttraction.name }}
          </el-descriptions-item>
          <el-descriptions-item label="地址">
            {{ currentAttraction.address || '暂无地址' }}
          </el-descriptions-item>
          <el-descriptions-item label="景点描述">
            {{ currentAttraction.description || '暂无描述' }}
          </el-descriptions-item>
          <el-descriptions-item label="门票价格">
            ¥{{ currentAttraction.ticket_price }}
          </el-descriptions-item>
          <el-descriptions-item label="建议游览时间">
            {{ currentAttraction.visit_time ? currentAttraction.visit_time + '分钟' : '暂无' }}
          </el-descriptions-item>
          <el-descriptions-item label="景点热度">
            <el-rate
              :model-value="currentAttraction.popularity / 20"
              disabled
              show-score
              text-color="#ff9900"
            />
          </el-descriptions-item>
          <el-descriptions-item label="经纬度">
            经度: {{ currentAttraction.longitude }}, 纬度: {{ currentAttraction.latitude }}
          </el-descriptions-item>
        </el-descriptions>
        
        <!-- 景点图片 -->
        <el-divider>景点图片</el-divider>
        <div v-loading="imagesLoading">
          <!-- 所有用户都可以查看图片 -->
          <div v-if="attractionImages.length > 0" class="image-gallery">
            <el-image
              v-for="(img, index) in attractionImages"
              :key="index"
              :src="img.url"
              :preview-src-list="attractionImages.map(i => i.url)"
              fit="cover"
              style="width: 150px; height: 150px; margin: 5px; border-radius: 4px;"
            />
          </div>
          <div v-if="attractionImages.length === 0 && !imagesLoading" class="empty-images">
            <el-empty description="暂无图片" :image-size="100" />
          </div>
        </div>
        
        <!-- 景点评价（普通用户可以评价，管理员不应该评价） -->
        <el-divider v-if="isAuthenticated && !isAdmin">用户评价</el-divider>
        <div v-if="isAuthenticated && !isAdmin" class="review-section">
          <el-form :model="reviewForm" label-width="80px">
            <el-form-item label="评分">
              <el-rate v-model="reviewForm.rating" />
            </el-form-item>
            <el-form-item label="评价内容">
              <el-input
                v-model="reviewForm.content"
                type="textarea"
                :rows="3"
                placeholder="分享您的游览体验..."
              />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitReview" :loading="reviewing">
                提交评价
              </el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-dialog>

    <!-- 地图对话框 -->
    <el-dialog
      v-model="mapDialogVisible"
      :title="currentAttraction?.name + ' - 位置地图'"
      width="900px"
    >
      <div id="attraction-map" style="width: 100%; height: 500px;"></div>
    </el-dialog>
  </div>
</template>

<script>
import { ref, onMounted, watch, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
import {
  Search,
  Refresh,
  PictureRounded,
  Location,
  Money,
  Clock,
  View as ViewIcon,
  MapLocation,
  Star,
  StarFilled,
  Filter,
  CircleCheck,
  Delete
} from '@element-plus/icons-vue'
import TravelAPI from '../api/travel'
import { AMapUtil } from '../utils/amap'
import LoadingSkeleton from '../components/LoadingSkeleton.vue'
import LazyImage from '../components/LazyImage.vue'
import { debounce } from '../utils/debounce'
import logger from '../utils/logger'
import { getImageUrl as getImageUrlUtil } from '../utils/apiConfig'

export default {
  name: 'Attractions',
  components: {
    Search,
    Location,
    Money,
    Clock,
    LoadingSkeleton,
    LazyImage
  },
  setup() {
    const router = useRouter()
    const store = useStore()
    const loading = ref(false)
    
    // 判断是否为管理员
    const isAdmin = computed(() => store.getters.isAdmin)
    const imagesLoading = ref(false)
    const attractions = ref([])
    const allAttractions = ref([])  // 保存所有景点数据用于前端筛选
    const searchKeyword = ref('')
    const selectedCategory = ref('')
    const sortBy = ref('')
    const currentPage = ref(1)
    const pageSize = ref(12)
    const total = ref(0)
    const mapDialogVisible = ref(false)
    const detailDialogVisible = ref(false)
    const currentAttraction = ref(null)
    const amapUtil = ref(null)
    const attractionImages = ref([])

    const reviewForm = ref({
      rating: 5,
      content: ''
    })
    const reviewing = ref(false)
    const isAuthenticated = computed(() => store.getters.isAuthenticated)
    const favoritingAttractions = ref({})  // 记录每个景点的收藏状态
    const selectedAttractions = ref([])  // 批量选择的景点ID列表
    const batchFavoriting = ref(false)  // 批量收藏加载状态

    const fetchAttractions = async () => {
      loading.value = true
      try {
        // 获取所有景点数据（不分页，用于前端筛选和排序）
        const params = {
          page_size: 1000  // 获取足够多的数据
        }

        const data = await TravelAPI.getAttractions(params)
        
        let attractionsList = []
        if (Array.isArray(data)) {
          attractionsList = data
          total.value = data.length
        } else {
          attractionsList = data.results || []
          total.value = data.count || 0
        }
        
        // 简化图片处理逻辑
        const processedAttractions = attractionsList.map(attr => {
          let primaryImage = null

          // 直接使用后端返回的数据
          if (attr.primary_image && attr.primary_image.startsWith('http')) {
            primaryImage = attr.primary_image
          } else if (attr.amap_images && Array.isArray(attr.amap_images) && attr.amap_images.length > 0) {
            primaryImage = attr.amap_images[0]
          } else if (attr.amap_image_urls && Array.isArray(attr.amap_image_urls) && attr.amap_image_urls.length > 0) {
            primaryImage = attr.amap_image_urls[0]
          }

          return {
            ...attr,
            primary_image: primaryImage,
            amap_images: attr.amap_images || attr.amap_image_urls || []
          }
        })
        
        // 保存所有景点数据
        allAttractions.value = processedAttractions
        // 应用筛选和排序
        applyFiltersAndSort()
      } catch (error) {
        import('../utils/errorHandler').then(({ handleApiError }) => {
          handleApiError(error, { defaultMessage: '获取景点列表失败' })
        })
        let errorMessage = '获取景点列表失败'
        if (error.response) {
          const status = error.response.status
          if (status === 500) {
            errorMessage = '服务器错误，请稍后重试'
          } else if (error.response.data?.error) {
            errorMessage = error.response.data.error
          } else if (error.response.data?.detail) {
            errorMessage = error.response.data.detail
          }
        } else if (error.message) {
          if (error.message.includes('timeout')) {
            errorMessage = '请求超时，请检查网络连接'
          } else if (error.message.includes('Network Error')) {
            errorMessage = '网络错误，请检查网络连接'
          }
        }
        ElMessage.error(errorMessage)
      } finally {
        loading.value = false
      }
    }

    // 应用筛选和排序
    const applyFiltersAndSort = () => {
      let filtered = [...allAttractions.value]

      // 分类筛选
      if (selectedCategory.value) {
        filtered = filtered.filter(attr => attr.category === selectedCategory.value)
      }

      // 搜索筛选
      if (searchKeyword.value) {
        const keyword = searchKeyword.value.toLowerCase()
        filtered = filtered.filter(attr =>
          attr.name.toLowerCase().includes(keyword) ||
          (attr.address && attr.address.toLowerCase().includes(keyword))
        )
      }

      // 排序
      if (sortBy.value) {
        switch (sortBy.value) {
          case 'rating':
            filtered.sort((a, b) => (b.rating || 0) - (a.rating || 0))
            break
          case 'popularity':
            filtered.sort((a, b) => (b.popularity || 0) - (a.popularity || 0))
            break
          case 'price_asc':
            filtered.sort((a, b) => (a.ticket_price || 0) - (b.ticket_price || 0))
            break
          case 'price_desc':
            filtered.sort((a, b) => (b.ticket_price || 0) - (a.ticket_price || 0))
            break
          case 'newest':
            filtered.sort((a, b) => {
              const dateA = new Date(a.created_at || 0)
              const dateB = new Date(b.created_at || 0)
              return dateB - dateA
            })
            break
        }
      }

      // 更新总数和当前页数据
      total.value = filtered.length
      const start = (currentPage.value - 1) * pageSize.value
      const end = start + pageSize.value
      attractions.value = filtered.slice(start, end)
    }

    const handleSearchDebounced = debounce((value) => {
      searchKeyword.value = value
      currentPage.value = 1
      applyFiltersAndSort()
    }, 500)

    const handleCategoryChange = () => {
      currentPage.value = 1
      applyFiltersAndSort()
    }

    const handleSortChange = () => {
      currentPage.value = 1
      applyFiltersAndSort()
    }

    const handleRefresh = () => {
      currentPage.value = 1
      searchKeyword.value = ''
      selectedCategory.value = ''
      sortBy.value = ''
      fetchAttractions()
    }

    const handleSizeChange = (val) => {
      pageSize.value = val
      currentPage.value = 1
      applyFiltersAndSort()
    }

    const handleCurrentChange = (val) => {
      currentPage.value = val
      applyFiltersAndSort()
    }

    // 获取分类显示名称
    const getCategoryDisplayName = (category) => {
      const names = {
        'cultural': '文化古迹',
        'natural': '自然风光',
        'modern': '现代建筑',
        'food': '美食购物'
      }
      return names[category] || category
    }

    // 获取排序显示名称
    const getSortDisplayName = (sort) => {
      const names = {
        'rating': '评分最高',
        'popularity': '热度最高',
        'price_asc': '价格最低',
        'price_desc': '价格最高',
        'newest': '最新添加'
      }
      return names[sort] || sort
    }

    const fetchAttractionImages = async (attractionId) => {
      if (!attractionId) return
      imagesLoading.value = true
      try {
        const response = await TravelAPI.getAttractionImages(attractionId)
        attractionImages.value = (response || []).map(img => ({
          id: img.id,
          url: getImageUrl(img.image),
          name: `image_${img.id}.jpg`
        }))
      } catch (error) {
        import('../utils/logger').then(({ logger }) => {
          logger.error('获取景点图片失败:', error)
        })
        attractionImages.value = []
        // 图片加载失败不显示错误提示，避免影响用户体验
      } finally {
        imagesLoading.value = false
      }
    }
    
    const getImageUrl = (url) => {
      return getImageUrlUtil(url)
    }
    
    const handleImageError = (event) => {
      // 使用默认图片
      const defaultImage = ''
      if (event.target && event.target.tagName === 'IMG') {
        event.target.src = defaultImage
      }
    }
    
    // 获取预览图片列表
    const getPreviewImageList = (attraction) => {
      const images = []
      
      // 添加主图
      if (attraction.primary_image) {
        images.push(attraction.primary_image)
      }
      
      // 添加高德地图图片
      if (attraction.amap_images && Array.isArray(attraction.amap_images)) {
        attraction.amap_images.forEach(url => {
          if (url && typeof url === 'string' && !images.includes(url)) {
            images.push(url)
          }
        })
      }
      
      // 添加amap_image_urls中的图片
      if (attraction.amap_image_urls && Array.isArray(attraction.amap_image_urls)) {
        attraction.amap_image_urls.forEach(url => {
          if (url && typeof url === 'string' && !images.includes(url)) {
            images.push(url)
          }
        })
      }
      
      return images.length > 0 ? images : []
    }
    
    
    const viewDetail = (attraction) => {
      currentAttraction.value = attraction
      detailDialogVisible.value = true
      if (attraction.id) {
        fetchAttractionImages(attraction.id)
      }
    }
    
    const goToDetailPage = (attraction) => {
      router.push(`/attractions/${attraction.id}`)
    }
    
    watch(detailDialogVisible, (visible) => {
      if (visible && currentAttraction.value?.id) {
        fetchAttractionImages(currentAttraction.value.id)
      }
    })

    const viewOnMap = async (attraction) => {
      currentAttraction.value = attraction
      mapDialogVisible.value = true

      await new Promise(resolve => setTimeout(resolve, 300))

      try {
        const apiKey = process.env.VUE_APP_AMAP_KEY || ''
        if (!apiKey || apiKey === 'your_amap_api_key') {
          ElMessage.error('高德地图API Key未配置，请在.env文件中设置VUE_APP_AMAP_KEY')
          return
        }

        if (!amapUtil.value) {
          amapUtil.value = new AMapUtil(apiKey)
        }

        if (amapUtil.value.map) {
          amapUtil.value.map.destroy()
          amapUtil.value.map = null
        }

        await new Promise(resolve => setTimeout(resolve, 100))
        
        await amapUtil.value.initMap('attraction-map', {
          center: [parseFloat(attraction.longitude), parseFloat(attraction.latitude)],
          zoom: 15
        })

        const infoContent = `
          <div style="padding: 10px;">
            <h3 style="margin: 0 0 10px 0;">${attraction.name}</h3>
            <p style="margin: 5px 0;"><strong>地址:</strong> ${attraction.address || '暂无'}</p>
            <p style="margin: 5px 0;"><strong>门票:</strong> ¥${attraction.ticket_price}</p>
          </div>
        `

        amapUtil.value.addMarkers([{
          lng: parseFloat(attraction.longitude),
          lat: parseFloat(attraction.latitude),
          title: attraction.name,
          info: infoContent
        }])
      } catch (error) {
        import('../utils/errorHandler').then(({ handleApiError }) => {
          handleApiError(error, { defaultMessage: '地图初始化失败' })
        })
        ElMessage.error('地图加载失败')
      }
    }

    const getCategoryType = (category) => {
      const types = {
        'cultural': 'warning',
        'natural': 'success',
        'modern': 'info',
        'food': 'danger'
      }
      return types[category] || 'info'
    }

    const getPopularityColor = (popularity) => {
      if (popularity >= 80) return '#67c23a'
      if (popularity >= 60) return '#e6a23c'
      if (popularity >= 40) return '#f56c6c'
      return '#909399'
    }

    // 批量操作相关函数
    const isSelected = (attractionId) => {
      return selectedAttractions.value.includes(attractionId)
    }

    const toggleSelection = (attractionId) => {
      const index = selectedAttractions.value.indexOf(attractionId)
      if (index > -1) {
        selectedAttractions.value.splice(index, 1)
      } else {
        selectedAttractions.value.push(attractionId)
      }
    }

    const clearSelection = () => {
      selectedAttractions.value = []
      ElMessage.info('已取消选择')
    }

    const handleCardClick = (attraction, event) => {
      // 如果点击的是复选框区域，不跳转
      if (event.target.closest('.card-checkbox')) {
        return
      }
      goToDetailPage(attraction)
    }

    const batchFavorite = async () => {
      if (selectedAttractions.value.length === 0) {
        ElMessage.warning('请先选择要收藏的景点')
        return
      }

      if (!isAuthenticated.value) {
        ElMessage.warning('请先登录')
        router.push('/login')
        return
      }

      batchFavoriting.value = true
      try {
        let successCount = 0
        let failCount = 0

        for (const attractionId of selectedAttractions.value) {
          try {
            await TravelAPI.favoriteAttraction(attractionId)
            successCount++
            // 更新景点收藏状态
            const attraction = attractions.value.find(a => a.id === attractionId)
            if (attraction) {
              attraction.is_favorited = true
            }
          } catch (error) {
            failCount++
            logger.error(`收藏景点 ${attractionId} 失败:`, error)
          }
        }

        if (successCount > 0) {
          ElMessage.success(`成功收藏 ${successCount} 个景点${failCount > 0 ? `，${failCount} 个失败` : ''}`)
        } else {
          ElMessage.error('批量收藏失败')
        }

        // 清空选择
        selectedAttractions.value = []
      } catch (error) {
        logger.error('批量收藏失败:', error)
        ElMessage.error('批量收藏失败，请稍后重试')
      } finally {
        batchFavoriting.value = false
      }
    }


    const submitReview = async () => {
      if (!currentAttraction.value?.id) {
        ElMessage.warning('请先选择景点')
        return
      }

      if (!reviewForm.value.rating) {
        ElMessage.warning('请选择评分')
        return
      }

      reviewing.value = true
      try {
        await TravelAPI.createAttractionReview(currentAttraction.value.id, {
          rating: reviewForm.value.rating,
          content: reviewForm.value.content
        })
        ElMessage.success('评价提交成功')
        reviewForm.value = {
          rating: 5,
          content: ''
        }
        // 刷新景点详情以显示新评价
        if (currentAttraction.value.id) {
          fetchAttractionImages(currentAttraction.value.id)
        }
      } catch (error) {
        import('../utils/errorHandler').then(({ handleApiError }) => {
          handleApiError(error, { defaultMessage: '提交评价失败' })
        })
        let errorMessage = '评价提交失败'
        if (error.response) {
          const status = error.response.status
          if (status === 400) {
            errorMessage = error.response.data?.error || error.response.data?.detail || '请求参数错误，请检查输入信息'
          } else if (status === 401) {
            errorMessage = '登录已过期，请重新登录'
          } else if (status === 403) {
            errorMessage = '没有权限提交评价'
          } else if (error.response.data?.error) {
            errorMessage = error.response.data.error
          } else if (error.response.data?.detail) {
            errorMessage = error.response.data.detail
          }
        } else if (error.message) {
          errorMessage = error.message
        }
        ElMessage.error(errorMessage)
      } finally {
        reviewing.value = false
      }
    }

    const toggleFavorite = async (attraction) => {
      if (!isAuthenticated.value) {
        ElMessage.warning('请先登录')
        router.push('/login')
        return
      }

      if (!attraction?.id) return

      favoritingAttractions.value[attraction.id] = true
      try {
        const isFavorited = attraction.is_favorited
        await TravelAPI.toggleFavorite(attraction.id, !isFavorited)
        // 更新景点列表中的收藏状态
        const index = attractions.value.findIndex(a => a.id === attraction.id)
        if (index !== -1) {
          attractions.value[index].is_favorited = !isFavorited
          attractions.value[index].favorites_count = isFavorited 
            ? (attractions.value[index].favorites_count || 1) - 1 
            : (attractions.value[index].favorites_count || 0) + 1
        }
        ElMessage.success(isFavorited ? '已取消收藏' : '收藏成功')
      } catch (error) {
        import('../utils/errorHandler').then(({ handleApiError }) => {
          handleApiError(error, { defaultMessage: '收藏操作失败' })
        })
        let errorMessage = '操作失败'
        if (error.response) {
          const status = error.response.status
          if (status === 401) {
            errorMessage = '登录已过期，请重新登录'
          } else if (status === 403) {
            errorMessage = '没有权限执行此操作'
          } else if (error.response.data?.error) {
            errorMessage = error.response.data.error
          } else if (error.response.data?.detail) {
            errorMessage = error.response.data.detail
          }
        } else if (error.message) {
          errorMessage = error.message
        }
        ElMessage.error(errorMessage)
      } finally {
        favoritingAttractions.value[attraction.id] = false
      }
    }

    onMounted(() => {
      fetchAttractions()
    })

    return {
      loading,
      imagesLoading,
      attractions,
      searchKeyword,
      currentPage,
      pageSize,
      total,
      mapDialogVisible,
      detailDialogVisible,
      currentAttraction,
      attractionImages,
      handleSearchDebounced,
      handleRefresh,
      handleCategoryChange,
      handleSortChange,
      handleSizeChange,
      handleCurrentChange,
      getCategoryDisplayName,
      getSortDisplayName,
      selectedCategory,
      sortBy,
      viewDetail,
      goToDetailPage,
      viewOnMap,
      getImageUrl,
      handleImageError,
      getPreviewImageList,
      getCategoryType,
      getPopularityColor,
      isAdmin,
      selectedAttractions,
      batchFavoriting,
      isSelected,
      toggleSelection,
      clearSelection,
      handleCardClick,
      batchFavorite,
      CircleCheck,
      Delete,
      isAuthenticated,
      Filter,
      reviewForm,
      reviewing,
      submitReview,
      toggleFavorite,
      favoritingAttractions,
      // 图标组件（用于:icon属性）
      Refresh,
      PictureRounded,
      ViewIcon,
      MapLocation,
      Star,
      StarFilled,
      LoadingSkeleton
    }
  }
}
</script>

<style scoped>
.attractions {
  padding: clamp(10px, 2vw, 20px);
  max-width: 1400px;
  margin: 0 auto;
  /* 南昌旅游特色：天空蓝渐变背景 */
  background: linear-gradient(135deg, #E3F2FD 0%, #BBDEFB 50%, #90CAF9 100%);
  min-height: 100vh;
  animation: fadeIn 0.5s ease-in;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.page-header {
  text-align: center;
  margin-bottom: 30px;
  padding: 30px 20px;
  background: linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(227,242,253,0.7) 100%);
  border-radius: 16px;
  backdrop-filter: blur(10px);
  box-shadow: 0 4px 20px rgba(200, 16, 46, 0.1);
  position: relative;
}

.page-header::before {
  content: '';
  position: absolute;
  top: -2px;
  left: -2px;
  right: -2px;
  bottom: -2px;
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 50%, #1E88E5 100%);
  border-radius: 16px;
  z-index: -1;
  opacity: 0.3;
  filter: blur(8px);
}

.page-title {
  font-size: clamp(24px, 4vw, 36px);
  font-weight: 700;
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.page-title .el-icon {
  font-size: 1.2em;
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.page-subtitle {
  font-size: clamp(14px, 2vw, 18px);
  color: #606266;
  font-weight: 500;
  letter-spacing: 0.5px;
}

.attractions :deep(.el-card) {
  border-radius: 12px;
  border: 1px solid rgba(200, 16, 46, 0.1);
  background: linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(227,242,253,0.8) 100%);
  backdrop-filter: blur(10px);
  box-shadow: 0 2px 12px rgba(200, 16, 46, 0.08);
  transition: all 0.3s ease;
}

.attractions :deep(.el-card:hover) {
  box-shadow: 0 8px 24px rgba(200, 16, 46, 0.16);
  transform: translateY(-4px);
  border-color: rgba(212, 175, 55, 0.3);
}

.action-bar :deep(.el-card) {
  background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(227,242,253,0.9) 100%);
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.attractions :deep(.el-card) {
  border-radius: 12px;
  border: 1px solid rgba(200, 16, 46, 0.1);
  background: linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(227,242,253,0.8) 100%);
  backdrop-filter: blur(10px);
  box-shadow: 0 2px 12px rgba(200, 16, 46, 0.08);
  transition: all 0.3s ease;
}

.attractions :deep(.el-card:hover) {
  box-shadow: 0 8px 24px rgba(200, 16, 46, 0.16);
  transform: translateY(-4px);
  border-color: rgba(212, 175, 55, 0.3);
}

.action-bar {
  margin-bottom: 24px;
  border-radius: 16px;
  border: 1px solid rgba(200, 16, 46, 0.12);
  background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(227,242,253,0.85) 100%);
  backdrop-filter: blur(12px);
  box-shadow: 0 4px 20px rgba(200, 16, 46, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
}

.action-bar::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #C8102E 0%, #D4AF37 50%, #1E88E5 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.action-bar:hover::before {
  opacity: 1;
}

.search-input :deep(.el-input__wrapper) {
  border-radius: 8px;
  transition: all 0.3s ease;
}

.search-input :deep(.el-input__wrapper):hover {
  box-shadow: 0 0 0 1px rgba(200, 16, 46, 0.3) inset;
}

.search-input :deep(.el-input__wrapper.is-focus) {
  box-shadow: 0 0 0 1px rgba(200, 16, 46, 0.5) inset;
}

.search-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
}

.search-icon {
  font-size: 18px;
  color: #606266;
  flex-shrink: 0;
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.filter-select {
  transition: all 0.3s ease;
}

.filter-select :deep(.el-input__wrapper):hover {
  box-shadow: 0 0 0 1px rgba(200, 16, 46, 0.3) inset;
}

.filter-select :deep(.el-input__wrapper.is-focus) {
  box-shadow: 0 0 0 1px rgba(200, 16, 46, 0.5) inset;
}

.filter-result-card {
  margin-bottom: 20px;
  border-radius: 12px;
  border: 1px solid rgba(200, 16, 46, 0.12);
  background: linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(227,242,253,0.8) 100%);
  backdrop-filter: blur(10px);
  box-shadow: 0 2px 12px rgba(200, 16, 46, 0.08);
}

.filter-result-info {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #606266;
}

.filter-result-info .el-icon {
  font-size: 16px;
  color: #C8102E;
}

.pagination-custom {
  margin-top: 24px;
}

.pagination-custom :deep(.el-pagination) {
  justify-content: center;
}

.refresh-button {
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 100%);
  border: none;
  font-weight: 600;
  transition: all 0.3s ease;
}

.refresh-button:hover {
  background: linear-gradient(135deg, #D4AF37 0%, #C8102E 100%);
  box-shadow: 0 4px 12px rgba(200, 16, 46, 0.3);
  transform: translateY(-2px);
}

.attractions-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 24px;
  margin-bottom: 30px;
}

/* 移动端优化 */
@media (max-width: 768px) {
  .attractions-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  
  .attractions {
    padding: 10px;
  }
  
  .page-title {
    font-size: 24px;
    flex-direction: column;
    gap: 8px;
  }
  
  .action-bar {
    padding: 16px;
  }
  
  .action-bar .el-input {
    width: 100%;
  }
}

.attraction-card {
  cursor: pointer;
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid rgba(200, 16, 46, 0.12);
  background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(227,242,253,0.85) 100%);
  backdrop-filter: blur(12px);
  box-shadow: 0 4px 20px rgba(200, 16, 46, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
  position: relative;
}

.attraction-card.selected {
  border: 2px solid #C8102E;
  box-shadow: 0 4px 16px rgba(200, 16, 46, 0.3);
  background: linear-gradient(135deg, rgba(200, 16, 46, 0.05) 0%, rgba(212, 175, 55, 0.05) 100%);
}

.card-checkbox {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 10;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(10px);
  border-radius: 50%;
  padding: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.batch-action-bar {
  margin-bottom: 20px;
  background: linear-gradient(135deg, rgba(200, 16, 46, 0.1) 0%, rgba(212, 175, 55, 0.1) 100%);
  border: 2px solid rgba(200, 16, 46, 0.3);
  animation: slideDown 0.3s ease;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.batch-action-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
}

.batch-info {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #606266;
}

.batch-info .el-icon {
  color: #C8102E;
  font-size: 18px;
}

.batch-info strong {
  color: #C8102E;
  font-weight: 600;
}

.batch-actions {
  display: flex;
  gap: 12px;
}

.attraction-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #C8102E 0%, #D4AF37 50%, #1E88E5 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 2;
}

.attraction-card:hover::before {
  opacity: 1;
}

.image-error-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 200px;
  background-color: #f5f7fa;
  color: #909399;
  font-size: 14px;
}

.image-error-slot .el-icon {
  font-size: 48px;
  margin-bottom: 8px;
}

.attraction-card:hover {
  transform: translateY(-6px) scale(1.01);
  box-shadow: 0 12px 32px rgba(200, 16, 46, 0.15), 0 4px 8px rgba(0, 0, 0, 0.1);
  border-color: rgba(212, 175, 55, 0.4);
}

.attraction-card > * {
  position: relative;
  z-index: 1;
}

.card-image {
  position: relative;
  width: 100%;
  height: 220px;
  overflow: hidden;
  background: linear-gradient(135deg, #E3F2FD 0%, #BBDEFB 50%, #90CAF9 100%);
}

.attraction-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.attraction-card:hover .attraction-image {
  transform: scale(1.1);
}

.image-slot,
.no-image {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: rgba(255, 255, 255, 0.8);
}

.image-slot .el-icon,
.no-image .el-icon {
  font-size: 48px;
  margin-bottom: 10px;
}

.card-badge {
  position: absolute;
  top: 10px;
  right: 10px;
}

.card-content {
  padding: 20px;
  background: transparent;
}

.attraction-name {
  margin: 0 0 12px 0;
  font-size: 20px;
  font-weight: 700;
  background: linear-gradient(135deg, #303133 0%, #606266 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: all 0.3s ease;
}

.attraction-card:hover .attraction-name {
  background: linear-gradient(135deg, #C8102E 0%, #D4AF37 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  transform: translateX(2px);
}

.attraction-info {
  margin-bottom: 15px;
}

.info-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  font-size: 14px;
  color: #606266;
}

.info-item .el-icon {
  margin-right: 8px;
  color: #909399;
}

.text-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

.card-footer {
  margin-top: 15px;
  padding-top: 15px;
  border-top: 1px solid rgba(200, 16, 46, 0.1);
}

.rating-section {
  margin-bottom: 10px;
}

.popularity-section {
  display: flex;
  align-items: center;
  gap: 10px;
}

.popularity-section .el-progress {
  flex: 1;
}

.popularity-text {
  font-size: 12px;
  color: #909399;
  white-space: nowrap;
}

.card-actions {
  padding: 12px 16px;
  border-top: 1px solid rgba(200, 16, 46, 0.08);
  display: flex;
  gap: 10px;
  background: rgba(255, 255, 255, 0.5);
}

.card-actions .el-button {
  flex: 1;
  transition: all 0.3s ease;
}

.card-actions .el-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(200, 16, 46, 0.2);
}

.empty-images {
  padding: 40px 0;
  text-align: center;
}

.image-gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
}

.review-section {
  margin-top: 20px;
  padding: 20px;
  background: #f5f7fa;
  border-radius: 4px;
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .attractions-grid {
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
  }
}

@media (max-width: 768px) {
  .attractions {
    padding: 10px;
  }

  .attractions-grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .card-image {
    height: 180px;
  }

  .card-content {
    padding: 15px;
  }

  .attraction-name {
    font-size: 18px;
  }

  .card-actions {
    flex-direction: column;
    gap: 8px;
  }

  .card-actions .el-button {
    width: 100%;
  }
}

@media (max-width: 480px) {
  .attractions {
    padding: 8px;
  }

  .card-image {
    height: 160px;
  }

  .attraction-name {
    font-size: 16px;
  }
}
</style>
